#include "ChessBoard.h"
#include "Utilities.h"
#include "Knight.h"

ChessBoard::ChessBoard(int width, int height, LPDIRECT3DDEVICE9 d3ddv)
{

	// Init variables
	m_iWidth		= width;
	m_iHeight		= height;
	m_bDone			= false;
	m_fTimeElapse	= 0;
	m_pD3Ddv		= d3ddv;
	m_iStartX		= 0;
	m_iStartY		= 0;
	m_iEndX			= 0;
	m_iEndY			= 0;
	m_iType			= ChessBoard::TYPE_FREE;

	// Init array step
	m_pArrStep = new int* [MAX_CHESS_WIDTH * MAX_CHESS_HEIGHT];
	for (int i = 0, size = MAX_CHESS_WIDTH * MAX_CHESS_HEIGHT; i < size; i++)
	{
		m_pArrStep[i] = new int[2];
	}
	

	// Init board
	m_ppBoard = new int* [MAX_CHESS_HEIGHT];
	for (int i = 0; i < MAX_CHESS_HEIGHT; i++)
	{
		m_ppBoard[i] = new int[MAX_CHESS_WIDTH];
		for (int j = 0; j < MAX_CHESS_WIDTH; j++)
		{
			m_ppBoard[i][j] = ChessBoard::NONE;
		}
	}


	// Init available move
	m_ppAvailMove = new int* [MAX_CHESS_WIDTH];
	for (int i = 0; i < MAX_CHESS_WIDTH; i++)
	{
		m_ppAvailMove[i] = new int[MAX_CHESS_HEIGHT];
	}


	for (int i = 0; i < m_iHeight; i++)
	{
		for (int j = 0; j < m_iWidth; j++)
		{
			int count = 0;
			for (int l = 0; l < Knight::MAX_MOVE; l++)
			{
				if ((j + Knight::MOVE_HOR[l] >= 0) && (j + Knight::MOVE_HOR[l] < m_iWidth)
					&& (i + Knight::MOVE_VER[l] >= 0) && (i + Knight::MOVE_VER[l] < m_iHeight))
				{
					count++;
				}
			}
			m_ppAvailMove[i][j] = count;
		}
	}
}



void ChessBoard::SetSize(int width, int height)
{
	m_iWidth = width;
	m_iHeight = height;
}


void ChessBoard::InitChessBoard()
{
	D3DXCreateTextureFromFile(m_pD3Ddv, Cb_Name_White, &m_pTexWhite);
	D3DXCreateTextureFromFile(m_pD3Ddv, Cb_Name_Black, &m_pTexBlack);
	D3DXCreateTextureFromFile(m_pD3Ddv, Cb_Name_Orange, &m_pTexOrange);

	CVertex *pVertexBuffer = NULL;
	int count = 0;


	m_pD3Ddv->CreateVertexBuffer(4 * sizeof(CVertex), D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC, FVF_TEX, D3DPOOL_DEFAULT, &m_pVb, NULL);
	m_pVb->Lock(0, 4 * sizeof(CVertex), (void**)&pVertexBuffer, D3DLOCK_DISCARD);
	
	// Front face
	pVertexBuffer[count++]	=	CVertex( -10.0f, 0.0f, -10.0f, 0.00f, 1.00f );
	pVertexBuffer[count++]	=	CVertex( -10.0f, 0.0f,  10.0f, 0.00f, 0.00f );
	pVertexBuffer[count++]	=	CVertex(  10.0f, 0.0f, -10.0f, 1.00f, 1.00f );
	pVertexBuffer[count++]	=	CVertex(  10.0f, 0.0f,  10.0f, 1.00f, 0.00f );

	m_pVb->Unlock();
}


void ChessBoard::SetRenderState()
{
	m_pD3Ddv->SetRenderState(D3DRS_ZENABLE, TRUE);
	m_pD3Ddv->SetStreamSource(0, m_pVb, 0, sizeof(CVertex));
	
	m_pD3Ddv->SetFVF(FVF_TEX);
}


void ChessBoard::DrawChessBoard()
{
	D3DXMatrixIdentity(&m_mtxWorld);

	int offset = 0;

	m_pD3Ddv->SetTexture(0, m_pTexWhite);
	for (int i = 0; i < m_iHeight; i++)
	{
		offset = (i % 2 == 0) ? 1 : 0;
		for (int j = 0; j < m_iWidth - offset; j += 2)
		{
			D3DXMatrixTranslation(&m_mtxWorld, (j + offset) * 20, 0, (i - m_iHeight) * 20);
			m_pD3Ddv->SetTransform(D3DTS_WORLD, &m_mtxWorld);
			m_pD3Ddv->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
		}
	}

	m_pD3Ddv->SetTexture(0, m_pTexBlack);
	for (int i = 0; i < m_iHeight; i++)
	{
		offset = (i % 2 == 0) ? 0 : 1;
		for (int j = 0; j < m_iWidth - offset; j += 2)
		{
			D3DXMatrixTranslation(&m_mtxWorld, (j + offset) * 20, 0, (i - m_iHeight) * 20);
			m_pD3Ddv->SetTransform(D3DTS_WORLD, &m_mtxWorld);
			m_pD3Ddv->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
		}
	}


	m_pD3Ddv->SetTexture(0, m_pTexOrange);
	for (int i = 0; i < m_iHeight; i++)
	{
		for (int j = 0; j < m_iWidth; j++)
		{
			if (m_ppBoard[i][j] == ChessBoard::CHECK)
			{
				D3DXMatrixTranslation(&m_mtxWorld, j * 20, 0, -(i + 1) * 20);
				m_pD3Ddv->SetTransform(D3DTS_WORLD, &m_mtxWorld);
				m_pD3Ddv->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
			}
		}
	}
}


ChessBoard::~ChessBoard()
{
	for (int i = 0; i < m_iHeight; i++)
	{
		delete[] m_ppAvailMove[i];
		delete[] m_ppBoard[i];
		delete[] m_pArrStep[i];
	}

	delete[] m_ppAvailMove;
	delete[] m_ppBoard;
	delete[] m_pArrStep;
}


void ChessBoard::UpdateAvailMove(int x, int y, bool increase)
{
	int value = increase ? 1 : -1;
	for (int l = 0; l < Knight::MAX_MOVE; l++)
	{
		if ((x + Knight::MOVE_HOR[l] >= 0) && (x + Knight::MOVE_HOR[l] < m_iWidth)
			&& (y + Knight::MOVE_VER[l] >= 0) && (y + Knight::MOVE_VER[l] < m_iHeight))
		{
			if (m_ppBoard[y + Knight::MOVE_VER[l]][x + Knight::MOVE_HOR[l]] == ChessBoard::NONE)
			{
				m_ppAvailMove[y + Knight::MOVE_VER[l]][x + Knight::MOVE_HOR[l]] += value;
			}
		}
	}
	m_ppAvailMove[y][x] += value;
}


void ChessBoard::CalcMovement(int step, int x, int y)
{
	if (m_bDone)
		return;

	if (step == m_iWidth * m_iHeight)
	{
		switch (m_iType)
		{
		case ChessBoard::TYPE_FREE:
			m_bDone = true;
			break;
		case ChessBoard::TYPE_CLOSED_TOUR:
			for (int l = 0; l < Knight::MAX_MOVE; l++)
			{
				if ((x + Knight::MOVE_HOR[l] == m_iStartX) && (y + Knight::MOVE_VER[l] == m_iStartY))
				{
					m_bDone = true;
				}
			}
			break;
		}
	}
	else
	{
		int numAvailMove = 0;
		int availMove[Knight::MAX_MOVE][3];

		for (int l = 0; l < Knight::MAX_MOVE; l++)
		{
			if ((x + Knight::MOVE_HOR[l] >= 0) && (x + Knight::MOVE_HOR[l] < m_iWidth)
				&& (y + Knight::MOVE_VER[l] >= 0) && (y + Knight::MOVE_VER[l] < m_iHeight)
				&& (m_ppBoard[y + Knight::MOVE_VER[l]][x + Knight::MOVE_HOR[l]] == ChessBoard::NONE))
			{
				availMove[numAvailMove][0] = m_ppAvailMove[y + Knight::MOVE_VER[l]][x + Knight::MOVE_HOR[l]];	// Available movement				
				availMove[numAvailMove][1] = x + Knight::MOVE_HOR[l];											// X position
				availMove[numAvailMove][2] = y + Knight::MOVE_VER[l];											// Y position
				numAvailMove++;
			}
		}


		for (int i = 0, size = numAvailMove - 1; i < size; i++)
		{
			for (int j = i + 1; j < numAvailMove; j++)
			{
				if (availMove[i][0] > availMove[j][0])
				{
					Swap(availMove[i][0], availMove[j][0]);
					Swap(availMove[i][1], availMove[j][1]);
					Swap(availMove[i][2], availMove[j][2]);
				}
			}
		}


		UpdateAvailMove(x, y, false);


		for (int i = 0; i < numAvailMove; i++)
		{
			if (m_bDone)
				return;

			m_ppBoard[y][x] = ChessBoard::CHECK;

			m_pArrStep[step][0] = availMove[i][1];
			m_pArrStep[step][1] = availMove[i][2];
			CalcMovement(step + 1, availMove[i][1], availMove[i][2]);

			m_ppBoard[y][x] = ChessBoard::NONE;
		}


		UpdateAvailMove(x, y, true);
	}
}


void ChessBoard::CalcMoveMent()
{
	float startTime = timeGetTime();
	CalcMovement(1, m_iStartX, m_iStartY);
	m_fTimeElapse = timeGetTime() - startTime;
	ResetAll();
}


void ChessBoard::SetKnightTour(int startX, int startY, int endX, int endY, int type)
{
	m_iStartX	= startX;
	m_iStartY	= startY;
	m_iEndX		= endX;
	m_iEndY		= endY;
	m_iType		= type;
	m_bDone		= false;

	ResetAll();

	m_pArrStep[0][0] = m_iStartX;
	m_pArrStep[0][1] = m_iStartY;

}


float ChessBoard::GetTimeElapse()
{
	return m_fTimeElapse;
}


int ChessBoard::GetType()
{
	return m_iType;
}


int ChessBoard::GetWidth()
{
	return m_iWidth;
}


int ChessBoard::GetHeight()
{
	return m_iHeight;
}


void ChessBoard::ResetAll()
{
	for (int i = 0; i < m_iWidth; i++)
	{
		for (int j = 0; j < m_iHeight; j++)
		{
			m_ppBoard[i][j] = ChessBoard::NONE;
		}
	}
	m_ppBoard[m_iStartY][m_iStartX] = ChessBoard::CHECK;

	for (int i = 0; i < m_iHeight; i++)
	{
		for (int j = 0; j < m_iWidth; j++)
		{
			int count = 0;
			for (int l = 0; l < Knight::MAX_MOVE; l++)
			{
				if ((j + Knight::MOVE_HOR[l] >= 0) && (j + Knight::MOVE_HOR[l] < m_iWidth)
					&& (i + Knight::MOVE_VER[l] >= 0) && (i + Knight::MOVE_VER[l] < m_iHeight))
				{
					count++;
				}
			}

			m_ppAvailMove[i][j] = count;
		}
	}
}

